﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using FATCASchema.Utilities;
using System.Xml.Linq;
using FATCASchema.Nodes;

public partial class FATCA_OECD
{
    [XmlIgnoreAttribute]
    public List<string> ValidationMessages { get; private set; }
    [XmlIgnoreAttribute]
    public string ValidatedXML { get; private set; }
    [XmlIgnoreAttribute]
    private static XmlReaderSettings _settings = null;
    [XmlIgnoreAttribute]
    private static string _schemaVersion;

    public static string GetSchemaVersion()
    {
        LoadSchemas();
        return _schemaVersion;
    }
    private static void LoadSchemas()
    {
        if (_settings == null)
        {
            _settings = new XmlReaderSettings();

            string[] files = Directory.GetFiles("Schema", "*.xsd");
            foreach (var path in files)
            {
                using (FileStream fs = new FileStream(path, FileMode.Open))
                {
                    XmlSchema schema = XmlSchema.Read(fs, new ValidationEventHandler((s, a) => { }));
                    _settings.Schemas.Add(schema);
                    if (path.ToLower().Contains("fatcaxml"))
                        _schemaVersion = schema.Version;
                }
            }
        }
    }

    #region SaveXML
    public void SaveXML(string filePath)
    {
        if (ValidatedXML != null && ValidatedXML.Trim().Length == 0)
            return;

        using (StreamWriter file = new System.IO.StreamWriter(filePath))
        {
            file.Write(ValidatedXML);
        }

    }
    #endregion SaveXML

    public FATCA_OECDNode GetTree()
    {
        return new FATCA_OECDNode(this);
    }

    #region GetFriendlyEnumName
    public static string GetFriendlyEnumName(FatcaAcctPoolReportType_EnumType item)
    {

        switch(item)
        {
            case FatcaAcctPoolReportType_EnumType.FATCA201:
                return "Recalcitrant (US Indicia)";
            case FatcaAcctPoolReportType_EnumType.FATCA202:
                return "Recalcitrant (no US Indicia)";
            case FatcaAcctPoolReportType_EnumType.FATCA203:
                return "Dormant accounts";
            case FatcaAcctPoolReportType_EnumType.FATCA204:
                return "NPFFI";
            case FatcaAcctPoolReportType_EnumType.FATCA205:
                return "Recalcitrant US persons";
            case FatcaAcctPoolReportType_EnumType.FATCA206:
                return "Recalcitrant passive NFFEs";
        }

        return string.Empty;
    }

    public static string GetFriendlyEnumName(FatcaDocTypeIndic_EnumType item)
    {

        switch (item)
        {
            case FatcaDocTypeIndic_EnumType.FATCA1:
                return "New Data";
            case FatcaDocTypeIndic_EnumType.FATCA2:
                return "Corrected Data";
            case FatcaDocTypeIndic_EnumType.FATCA3:
                return "Void Data";
            case FatcaDocTypeIndic_EnumType.FATCA4:
                return "Amended Data";
            case FatcaDocTypeIndic_EnumType.FATCA11:
                return "New Test Data";
            case FatcaDocTypeIndic_EnumType.FATCA12:
                return "Corrected Test Data";
            case FatcaDocTypeIndic_EnumType.FATCA13:
                return "Void Test Data";
            case FatcaDocTypeIndic_EnumType.FATCA14:
                return "Amended Test Data";
        }

        return string.Empty;
    }

    public static string GetFriendlyEnumName(FatcaAcctHolderType_EnumType item)
    {

        switch (item)
        {
            case FatcaAcctHolderType_EnumType.FATCA101:
                return "Owner-Documented FI with specified US owner(s)";
            case FatcaAcctHolderType_EnumType.FATCA102:
                return "Passive Non-Financial Entity with substantial US owner(s)";
            case FatcaAcctHolderType_EnumType.FATCA103:
                return "Non-Participating FI";
            case FatcaAcctHolderType_EnumType.FATCA104:
                return "Specified US Person";
            case FatcaAcctHolderType_EnumType.FATCA105:
                return "Direct Reporting NFFE";
        }

        return string.Empty;
    }

    public static string GetFriendlyEnumName(FatcaPaymentType_EnumType item)
    {

        switch (item)
        {
            case FatcaPaymentType_EnumType.FATCA501:
                return "Dividends";
            case FatcaPaymentType_EnumType.FATCA502:
                return "Interest";
            case FatcaPaymentType_EnumType.FATCA503:
                return "Gross Proceeds/Redemptions";
            case FatcaPaymentType_EnumType.FATCA504:
                return "Other - FATCA";
        }

        return string.Empty;
    }

    public static string GetFriendlyEnumName(MessageType_EnumType item)
    {

        switch (item)
        {
            case MessageType_EnumType.FATCA:
                return "FATCA";
        }

        return string.Empty;
    }

    public static string GetFriendlyEnumName(OECDNameType_EnumType item)
    {

        switch (item)
        {
            case OECDNameType_EnumType.OECD201:
                return "SMF Alias Or Other";
            case OECDNameType_EnumType.OECD202:
                return "Individual";
            case OECDNameType_EnumType.OECD203:
                return "Alias";
            case OECDNameType_EnumType.OECD204:
                return "Nickname";
            case OECDNameType_EnumType.OECD205:
                return "AKA";
            case OECDNameType_EnumType.OECD206:
                return "DBA";
            case OECDNameType_EnumType.OECD207:
                return "Legal";
            case OECDNameType_EnumType.OECD208:
                return "At Birth";
        }

        return string.Empty;
    }

    public static string GetFriendlyEnumName(OECDLegalAddressType_EnumType item)
    {

        switch (item)
        {
            case OECDLegalAddressType_EnumType.OECD301:
                return "Residential Or Business";
            case OECDLegalAddressType_EnumType.OECD302:
                return "Residential";
            case OECDLegalAddressType_EnumType.OECD303:
                return "Business";
            case OECDLegalAddressType_EnumType.OECD304:
                return "Registered Office";
            case OECDLegalAddressType_EnumType.OECD305:
                return "Unspecified";
        }

        return string.Empty;
    }

    public static List<System.Collections.DictionaryEntry> GetFriendlyEnumList(Type type)
    {
        List<System.Collections.DictionaryEntry> list = new List<System.Collections.DictionaryEntry>();

        foreach (var value in Enum.GetValues(type))
        {
            if (value is FatcaPaymentType_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((FatcaPaymentType_EnumType)value)));
            if (value is FatcaAcctHolderType_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((FatcaAcctHolderType_EnumType)value)));
            if (value is FatcaDocTypeIndic_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((FatcaDocTypeIndic_EnumType)value)));
            if (value is FatcaAcctPoolReportType_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((FatcaAcctPoolReportType_EnumType)value)));
            if (value is MessageType_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((MessageType_EnumType)value)));
            if (value is OECDNameType_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((OECDNameType_EnumType)value)));
            if (value is OECDLegalAddressType_EnumType)
                list.Add(new System.Collections.DictionaryEntry(value, GetFriendlyEnumName((OECDLegalAddressType_EnumType)value)));
        }

        return list;
    }
    #endregion GetFriendlyEnumName

    #region Generate XML
    public bool GenerateXML()
    {
        ValidationMessages = new List<string>();
        ValidatedXML = string.Empty;

        string tempXML = SerializeToXML();

        if (Validate(tempXML) && ValidationMessages.Count == 0)
            ValidatedXML = tempXML;

        if (ValidatedXML.Length > 0)
            return true;
        else
            return false;
    }

    private string SerializeToXML()
    {
        XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(FATCA_OECD));
        StringWriterWithEncoding xml = new StringWriterWithEncoding(Encoding.UTF8);

        x.Serialize(XmlWriter.Create(xml), this);

        return xml.ToString();
    }
    #endregion Generate XML

    #region Validate XML
    public bool Validate(string xml)
    {
        ValidationMessages = new List<string>();

        LoadSchemas();

        _settings.ValidationType = ValidationType.Schema;
        _settings.ValidationEventHandler += (s, a) =>
        {
            ValidationMessages.Add(a.Exception.Message);
        };

        ValidateSchema(xml);

        if (ValidationMessages.Count == 0)
            ValidateMandatoryFields(xml);

        if (ValidationMessages.Count == 0)
            return true;
        else
            return false;

    }

    private void ValidateSchema(string xml)
    {
        try
        {
            XmlReader reader = XmlReader.Create(new StringReader(xml), _settings);

            // Read the xml schema.
            while (reader.Read()) { }

            reader.Close();
        }
        catch (Exception ex)
        {
            ValidationMessages.Add(ex.Message);
        }
    }

    private void ValidateMandatoryFields(string xml)
    {
        try
        {
            var message = XDocument.Parse(xml);
            var rootElement = message.Descendants().FirstOrDefault(r => r.Name.LocalName.ToLower() == "fatca_oecd");
            if (rootElement == null)
                ValidationMessages.Add("Message is missing the FATCA_OECD element");

            var version = rootElement.Attributes().FirstOrDefault(r => r.Name.LocalName.ToLower() == "version");
            if (version == null || version.Value.Trim().Length == 0)
                ValidationMessages.Add("Message is missing the version attribute");

            var msgSpecElement = rootElement.Descendants().FirstOrDefault(r => r.Name.LocalName.ToLower() == "messagespec");
            if (msgSpecElement == null)
                ValidationMessages.Add("Message is missing the MessageSpec element");

            var addresses = rootElement.Descendants().Where(r => r.Name.LocalName.ToLower() == "address");
            foreach (var address in addresses)
            {
                var countryCode = address.Descendants().FirstOrDefault(r => r.Name.LocalName.ToLower() == "countrycode");
                if (countryCode == null || countryCode.Value.Trim().Length == 0)
                    ValidationMessages.Add("Message is missing the CountryCode element");
            }

            var sendingCompanyElement = msgSpecElement.Descendants().FirstOrDefault(r => r.Name.LocalName.ToLower() == "sendingcompanyin");
            if (sendingCompanyElement == null || sendingCompanyElement.Value.Trim().Length == 0)
                ValidationMessages.Add("Message is missing the SendingCompanyIN element");

            var accountHolders = rootElement.Descendants().Where(r => r.Name.LocalName.ToLower() == "accountholder");
            foreach (var accountholder in accountHolders)
            {
                var tinElement = accountholder.Descendants().FirstOrDefault(r => r.Name.LocalName.ToLower() == "tin");
                if (tinElement == null || tinElement.Value.Trim().Length == 0)
                    ValidationMessages.Add("Message is missing the TIN element");

                var individuals = accountholder.Descendants().Where(r => r.Name.LocalName.ToLower() == "individual");
                foreach (var individual in individuals)
                {
                    var birthElement = accountholder.Descendants().FirstOrDefault(r => r.Name.LocalName.ToLower() == "birthinfo");
                    if (birthElement == null)
                        ValidationMessages.Add("Message is missing the BirthInfo element");
                }

            }

            //add logic to validate additional mandatory FATCA fields here

            //ValidationMessages.Add(new ValidationMessage() { Message = ex.Message });
        }
        catch (Exception ex)
        {
            ValidationMessages.Add(ex.Message);
        }
    }
    #endregion Validate XML

    #region Generate Objects
    public static FATCA_OECD NewFiling()
    {
        #region GeneralInfo
        var filing = new FATCA_OECD();
        filing.version = FATCA_OECD.GetSchemaVersion();

        filing.MessageSpec = new MessageSpec_Type
        {
            MessageType = MessageType_EnumType.FATCA,
            Timestamp = DateTime.Now,
            TransmittingCountry = CountryCode_Type.KY
        };
        #endregion GeneralInfo

        var fatca = NewFATCAElement();

        filing.FATCA = new Fatca_Type[] { fatca };

        return filing;

    }

    public static Fatca_Type NewFATCAElement()
    {
        #region FATCA Element
        var fatca = new Fatca_Type();

        #region Reporting FI
        var reportingFI = new CorrectableOrganisationParty_Type();

        reportingFI.DocSpec = new DocSpec_Type();

        reportingFI.Name = new NameOrganisation_Type[] { new NameOrganisation_Type() };
        reportingFI.ResCountryCode = new CountryCode_Type[1];
        reportingFI.TIN = new TIN_Type[] { new TIN_Type() };

        fatca.ReportingFI = reportingFI;

        #endregion Reporting FI

        fatca.ReportingGroup = new Fatca_TypeReportingGroup[] { new Fatca_TypeReportingGroup() };

        #endregion FATCA Element

        return fatca;
    }

    public static FATCA_OECD NewSampleFiling(int individualCount, int entityCount)
    {
        #region GeneralInfo
        var filing = new FATCA_OECD();
        filing.version = FATCA_OECD.GetSchemaVersion();

        filing.MessageSpec = new MessageSpec_Type
        {
            //CorrMessageRefId = "",
            MessageRefId = "unique id",
            MessageType = MessageType_EnumType.FATCA,
            ReceivingCountry = CountryCode_Type.KY,
            ReportingPeriod = new DateTime(2014, 12, 31),
            SendingCompanyIN = "GIIN #",
            Timestamp = DateTime.Now,
            TransmittingCountry = CountryCode_Type.KY
        };
        #endregion GeneralInfo

        #region FATCA Element
        var fatca = new Fatca_Type();

        #region Reporting FI
        var reportingFI = new CorrectableOrganisationParty_Type();

        reportingFI.Address = new Address_Type[] 
                { 
                    new Address_Type 
                        { 
                            CountryCode = CountryCode_Type.KY,
                            Items = new AddressFix_Type[] 
                                { new AddressFix_Type 
                                    {
                                        BuildingIdentifier = "Cayman Corporate Center",
                                        City = "George Town",
                                        CountrySubentity = "Grand Cayman",
                                        FloorIdentifier = "4th Floor",
                                        PostCode = "KY1-1007",
                                        Street = "27 Hospitial Road"
                                    } 
                                }
                        }
                };


        reportingFI.DocSpec = new DocSpec_Type
        {
            DocTypeIndic = FatcaDocTypeIndic_EnumType.FATCA1,
            DocRefId = "unique id"
        };

        reportingFI.Name = new NameOrganisation_Type[] { new NameOrganisation_Type { Value = "Harmonic Fund Services" } };
        reportingFI.ResCountryCode = new CountryCode_Type[] { CountryCode_Type.KY };
        //reportingFI.TIN = new TIN_Type[] { new TIN_Type{ issuedBy = CountryCode_Type.US, issuedBySpecified = true, Value = "nnn-nnn-nnn"}};

        fatca.ReportingFI = reportingFI;

        #endregion Reporting FI

        #region Reporting Group
        #region Add individual
        List<CorrectableAccountReport_Type> accountReports = new List<CorrectableAccountReport_Type>();
        CorrectableAccountReport_Type accountReport = null;

        int i = 0;
        while (i < individualCount)
        {
            accountReport = new CorrectableAccountReport_Type();

            accountReport.AccountBalance = new MonAmnt_Type
            {
                Value = 1000000m,
                currCode = currCode_Type.USD
            };

            accountReport.AccountNumber = "Account #";

            var person = new PersonParty_Type();
            person.Name = new NamePerson_Type[] 
                {
                    new NamePerson_Type 
                    {
                        FirstName = new NamePerson_TypeFirstName { Value = "John" },
                        LastName = new NamePerson_TypeLastName { Value = "Doe" },
                        nameType = OECDNameType_EnumType.OECD202,
                        nameTypeSpecified = true
                    }
                };

            person.Address = new Address_Type[] 
                { 
                    new Address_Type 
                        { 
                            CountryCode = CountryCode_Type.KY,
                            Items = new AddressFix_Type[] 
                                { new AddressFix_Type 
                                    {
                                        BuildingIdentifier = "Cayman Corporate Center",
                                        City = "George Town",
                                        CountrySubentity = "Grand Cayman",
                                        FloorIdentifier = "4th Floor",
                                        PostCode = "KY1-1007",
                                        Street = "27 Hospitial Road"
                                    } 
                                }
                        }
                };

            person.BirthInfo = new PersonParty_TypeBirthInfo
            {
                BirthDate = new DateTime(1972, 6, 6),
                BirthDateSpecified = true,
                City = "New York",
                CountryInfo = new PersonParty_TypeBirthInfoCountryInfo { Item = CountryCode_Type.US }
            };

            person.TIN = new TIN_Type[] { new TIN_Type { issuedBy = CountryCode_Type.US, issuedBySpecified = true, Value = "nnn-nnn-nnn" } };

            accountReport.AccountHolder = new AccountHolder_Type
            {
                Items = new PersonParty_Type[] { person }
            };

            accountReport.DocSpec = new DocSpec_Type
            {
                DocTypeIndic = FatcaDocTypeIndic_EnumType.FATCA1,
                DocRefId = "unique id"
            };
            //accountReport.Payment;

            accountReports.Add(accountReport);

            i++;
        }


        #endregion Add individual

        #region Add Entity

        i = 0;
        while (i < entityCount)
        {
            accountReport = new CorrectableAccountReport_Type();

            accountReport.AccountBalance = new MonAmnt_Type
            {
                Value = 1000000m,
                currCode = currCode_Type.USD
            };

            accountReport.AccountNumber = "Account #";

            var org = new OrganisationParty_Type();
            org.Name = new NameOrganisation_Type[] 
                {
                    new NameOrganisation_Type 
                    {
                        Value = "Acme LTD"
                        //nameType = OECDNameType_EnumType.OECD207,
                        //nameTypeSpecified = true
                    }
                };

            org.Address = new Address_Type[] 
                { 
                    new Address_Type 
                        { 
                            CountryCode = CountryCode_Type.KY,
                            Items = new AddressFix_Type[] 
                                { new AddressFix_Type 
                                    {
                                        BuildingIdentifier = "Cayman Corporate Center",
                                        City = "George Town",
                                        CountrySubentity = "Grand Cayman",
                                        FloorIdentifier = "4th Floor",
                                        PostCode = "KY1-1007",
                                        Street = "27 Hospitial Road"
                                    } 
                                }
                        }
                };

            org.TIN = new TIN_Type[] { new TIN_Type { issuedBy = CountryCode_Type.US, issuedBySpecified = true, Value = "nnn-nnn-nnn" } };

            accountReport.AccountHolder = new AccountHolder_Type
            {
                Items = new object[] { org, FatcaAcctHolderType_EnumType.FATCA101 }
            };

            accountReport.DocSpec = new DocSpec_Type
            {
                DocTypeIndic = FatcaDocTypeIndic_EnumType.FATCA1,
                DocRefId = "unique id"
            };
            //accountReport.Payment;
            //accountReport.SubstantialOwner;

            accountReports.Add(accountReport);
            i++;
        }
        #endregion Add Entity

        #region Add Recalcitrant Pool
        List<CorrectablePoolReport_Type> poolReports = new List<CorrectablePoolReport_Type>();
        CorrectablePoolReport_Type poolReport = new CorrectablePoolReport_Type();

        poolReport.AccountCount = "10";
        poolReport.AccountPoolReportType = FatcaAcctPoolReportType_EnumType.FATCA201;
        poolReport.PoolBalance = new MonAmnt_Type { Value = 1000m, currCode = currCode_Type.USD };

        poolReport.DocSpec = new DocSpec_Type
        {
            DocTypeIndic = FatcaDocTypeIndic_EnumType.FATCA1,
            DocRefId = "unique id"
        };


        poolReports.Add(poolReport);
        #endregion Add Recalcitrant Pool

        fatca.ReportingGroup = new Fatca_TypeReportingGroup[] 
                { 
                    new Fatca_TypeReportingGroup { AccountReport = accountReports.ToArray(), PoolReport = poolReports.ToArray() }
                };
        #endregion Reporting Group

        #endregion FATCA Element

        filing.FATCA = new Fatca_Type[] { fatca };

        return filing;
    }
    #endregion Generate Objects

    public static object[] CreateNewReportingIndividual()
    {
        var individual = new PersonParty_Type();
        individual.Name = new NamePerson_Type[] { new NamePerson_Type() };
        individual.Address = new Address_Type[] { new Address_Type() };

        return new object[] { individual };
    }

    public static object[] CreateNewReportingOrganization()
    {
        var org = new OrganisationParty_Type();
        org.Name = new NameOrganisation_Type[] { new NameOrganisation_Type() };
        org.Address = new Address_Type[] { new Address_Type() };

        return new object[] { org, new AccountHolder_Type() };
    }

}

#region Wrapper Classes

namespace FATCASchema.Wrappers
{
    #region Address
    public class Address : IEquatable<Address>
    {
        public CountryCode_Type Country { get; set; }
        public OECDLegalAddressType_EnumType AddressType { get; set; }
        public string AddressText { get; set; }
        public bool IsFreeTextAddress { get; set; }
        public Address_Type FATCAAddress { get; set; }
        private Guid ID { get; set; }

        public Address()
        {
            ID = Guid.NewGuid();
            FATCAAddress = new Address_Type();
        }

        public void UpdateFriendlyFields()
        {
            Country = FATCAAddress.CountryCode;
            AddressType = FATCAAddress.legalAddressType;

            AddressFix_Type fixAddress = null;
            string freeAddress = string.Empty;

            if (FATCAAddress.Items != null && FATCAAddress.Items.Count() > 0)
            {
                foreach (var add in FATCAAddress.Items)
                {
                    if (add is AddressFix_Type && fixAddress == null)
                        fixAddress = (AddressFix_Type)add;
                    if (add is string && freeAddress == string.Empty)
                        freeAddress = add.ToString();
                }

                if (fixAddress != null)
                {
                    IsFreeTextAddress = false;
                    AddressText = fixAddress.Street + " " + fixAddress.City + " " + fixAddress.DistrictName + " " + fixAddress.CountrySubentity;
                }
                else
                {
                    IsFreeTextAddress = true;
                    AddressText = freeAddress;
                }
            }
        }

        public bool Equals(Address other)
        {
            if (other == null) return false;
            return this.ID.Equals(other.ID);
        }
    }
    public class Addresses : List<Address>
    {
        public static Addresses GetWrapperAddresses(Address_Type[] rawAddresses)
        {
            Addresses addresses = new Addresses();

            if (rawAddresses != null)
            {
                foreach (var rawAdd in rawAddresses)
                {
                    var add = new Address() { FATCAAddress = rawAdd };
                    add.UpdateFriendlyFields();
                    addresses.Add(add);
                }
            }

            return addresses;
        }

        public Address_Type[] ToFATCAArray()
        {
            return this.Select(r => r.FATCAAddress).ToArray();
        }

    }
    #endregion Address

    #region Payment
    public class Payment : IEquatable<Payment>
    {
        public FatcaPaymentType_EnumType PaymentType { get; set; }
        public currCode_Type Currency { get; set; }
        public decimal Amount { get; set; }
        public Payment_Type FATCAPayment { get; set; }
        private Guid ID { get; set; }

        public Payment()
        {
            ID = Guid.NewGuid();
            FATCAPayment = new Payment_Type();
        }

        public void UpdateFriendlyFields()
        {
            PaymentType = FATCAPayment.Type;
            Currency = FATCAPayment.PaymentAmnt.currCode;
            Amount = FATCAPayment.PaymentAmnt.Value;
        }

        public bool Equals(Payment other)
        {
            if (other == null) return false;
            return this.ID.Equals(other.ID);
        }
    }
    public class Payments : List<Payment>
    {
        public static Payments GetWrapperAddresses(Payment_Type[] rawpayments)
        {
            Payments payments = new Payments();

            if (rawpayments != null)
            {
                foreach (var rawPay in rawpayments)
                {
                    var pay = new Payment() { FATCAPayment = rawPay };
                    pay.UpdateFriendlyFields();
                    payments.Add(pay);
                }
            }

            return payments;
        }

        public Payment_Type[] ToFATCAArray()
        {
            return this.Select(r => r.FATCAPayment).ToArray();
        }

    }
    #endregion Payment

    #region OrgPartyWithType
    public class OrgPartyWithType : IEquatable<OrgPartyWithType>
    {
        public FatcaAcctHolderType_EnumType AccountType { get; set; }
        public OrganisationParty_Type Organization { get; set; }
        private Guid ID { get; set; }

        public OrgPartyWithType()
        {
            ID = Guid.NewGuid();
        }

        public void SetValues(object[] items)
        {
            foreach (var item in items)
            {
                if (item is OrganisationParty_Type)
                    Organization = (OrganisationParty_Type)item;
                if (item is FatcaAcctHolderType_EnumType)
                    AccountType = (FatcaAcctHolderType_EnumType)item;
            }
        }

        public bool Equals(OrgPartyWithType other)
        {
            if (other == null) return false;
            return this.ID.Equals(other.ID);
        }
    }
    #endregion OrgPartyWithType

    #region BirthInfo
    public class BirthInfo : IEquatable<BirthInfo>, System.ComponentModel.INotifyPropertyChanged
    {
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        // This method is called by the Set accessor of each property. 
        // The CallerMemberName attribute that is applied to the optional propertyName 
        // parameter causes the property name of the caller to be substituted as an argument. 
        private void NotifyPropertyChanged(String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }

        private DateTime? _birthDate;
        public DateTime? BirthDate 
        {
            get { return _birthDate; }
            set 
            {
                _birthDate = value;
                NotifyPropertyChanged();
            } 
        }

        private string _city;
        public string City
        {
            get { return _city; }
            set
            {
                _city = value;
                NotifyPropertyChanged();
            }
        }

        private string _subEntity;
        public string SubEntity
        {
            get { return _subEntity; }
            set
            {
                _subEntity = value;
                NotifyPropertyChanged();
            }
        }

        private CountryCode_Type? _countryCode;
        public CountryCode_Type? CountryCode 
        {
            get { return _countryCode; }
            set
            {
                _countryCode = value;
                _countryName = string.Empty;
                NotifyPropertyChanged();
            }
        }

        private string _countryName;
        public string CountryName 
        {
            get { return _countryName; }
            set 
            {
                _countryName = value;
                _countryCode = null;
                NotifyPropertyChanged();
            }
        }

        private Guid ID { get; set; }

        public BirthInfo()
        {
            ID = Guid.NewGuid();

        }

        public void SetValues(PersonParty_TypeBirthInfo info)
        {
            if (info == null)
            {
                BirthDate = null;
                City = string.Empty;
                SubEntity = string.Empty;
                CountryCode = null;
                CountryName = string.Empty;
            }
            else
            {
                BirthDate = info.BirthDate;
                City = info.City;
                SubEntity = info.CitySubentity;

                if (info.CountryInfo.Item is CountryCode_Type)
                {
                    CountryCode = (CountryCode_Type)info.CountryInfo.Item;
                    CountryName = string.Empty;
                }
                else if (info.CountryInfo.Item is string)
                {
                    CountryCode = null;
                    CountryName = (string)info.CountryInfo.Item;
                }
                else
                {
                    CountryCode = null;
                    CountryName = string.Empty;
                }
            }
        }

        public bool IsPopulated()
        {
            if (this.BirthDate != null)
                return true;
            if (this.City != string.Empty)
                return true;
            if (this.CountryCode != null)
                return true;
            if (this.CountryName != null)
                return true;
            if (this.SubEntity != null)
                return true;

            return false;
        }

        public PersonParty_TypeBirthInfo GetFATCABirthInto()
        {
            if (!this.IsPopulated())
                return null;

            var info = new PersonParty_TypeBirthInfo();

            if (this.BirthDate.HasValue)
            {
                info.BirthDate = this.BirthDate.Value;
                info.BirthDateSpecified = true;
            }

            info.City = this.City;
            info.CitySubentity = this.SubEntity;
            if (this.CountryCode.HasValue)
            {
                info.CountryInfo = new PersonParty_TypeBirthInfoCountryInfo();
                info.CountryInfo.Item = this.CountryCode.Value;
            }
            else if (this.CountryName != string.Empty)
            {
                info.CountryInfo = new PersonParty_TypeBirthInfoCountryInfo();
                info.CountryInfo.Item = this.CountryName;
            }

            return info;
        }

        public bool Equals(BirthInfo other)
        {
            if (other == null) return false;
            return this.ID.Equals(other.ID);
        }
    }
    #endregion BirthInfo

    #region PersonName
    public class PersonName : IEquatable<PersonName>, System.ComponentModel.INotifyPropertyChanged
    {
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        // This method is called by the Set accessor of each property. 
        // The CallerMemberName attribute that is applied to the optional propertyName 
        // parameter causes the property name of the caller to be substituted as an argument. 
        private void NotifyPropertyChanged(String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }

        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                _firstName = value;
                NotifyPropertyChanged();
            }
        }

        private string _generalSuffix;
        public string GeneralSuffix
        {
            get { return _generalSuffix; }
            set
            {
                _generalSuffix = value;
                NotifyPropertyChanged();
            }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set
            {
                _lastName = value;
                NotifyPropertyChanged();
            }
        }

        private string _namePrefix;
        public string NamePrefix
        {
            get { return _namePrefix; }
            set
            {
                _namePrefix = value;
                NotifyPropertyChanged();
            }
        }

        private string _precedingTitle;
        public string PrecedingTitle
        {
            get { return _precedingTitle; }
            set
            {
                _precedingTitle = value;
                NotifyPropertyChanged();
            }
        }

        private OECDNameType_EnumType? _nameType;
        public OECDNameType_EnumType? NameType
        {
            get { return _nameType; }
            set
            {
                _nameType = value;
                NotifyPropertyChanged();
            }
        }
 
        public bool NameTypeSpecified
        {
            get { return _nameType.HasValue; }
        }

        private List<GenericName> _generationIdentifier;
        public List<GenericName> GenerationIdentifier
        {
            get { return _generationIdentifier; }
            set
            {
                _generationIdentifier = value;
                NotifyPropertyChanged();
            }
        }

        private List<GenericName> _middleName;
        public List<GenericName> MiddleName
        {
            get { return _middleName; }
            set
            {
                _middleName = value;
                NotifyPropertyChanged();
            }
        }

        private List<GenericName> _suffix;
        public List<GenericName> Suffix
        {
            get { return _suffix; }
            set
            {
                _suffix = value;
                NotifyPropertyChanged();
            }
        }

        private List<GenericName> _title;
        public List<GenericName> Title
        {
            get { return _title; }
            set
            {
                _title = value;
                NotifyPropertyChanged();
            }
        } 
                
        private Guid ID { get; set; }

        public PersonName()
        {
            ID = Guid.NewGuid();

        }

        public void SetValues(NamePerson_Type info)
        {
            if (info == null)
            {
                this.FirstName = string.Empty;
                this.GeneralSuffix = string.Empty;
                this.GenerationIdentifier = new List<GenericName>();
                this.LastName = string.Empty;
                this.MiddleName = new List<GenericName>();
                this.NamePrefix = string.Empty;
                this.NameType = null;
                this.PrecedingTitle = string.Empty;
                this.Suffix = new List<GenericName>();
                this.Title = new List<GenericName>();
            }
            else
            {
                if (info.FirstName != null)
                    this.FirstName = info.FirstName.Value;
                if (info.GeneralSuffix != null)
                    this.GeneralSuffix = info.GeneralSuffix;

                if (info.GenerationIdentifier != null)
                    this.GenerationIdentifier = info.GenerationIdentifier.Select(r => new GenericName { Name = r }).ToList();
                else
                    this.GenerationIdentifier = new List<GenericName>();
                if (info.LastName != null)
                    this.LastName = info.LastName.Value;
                if (info.MiddleName != null)
                    this.MiddleName = info.MiddleName.Select(r => new GenericName { Name = r.Value }).ToList();
                else
                    this.MiddleName = new List<GenericName>();
                if (info.NamePrefix != null)
                    this.NamePrefix = info.NamePrefix.Value;               
                this.NameType = info.nameType;
                if (info.PrecedingTitle != null)
                    this.PrecedingTitle = info.PrecedingTitle;
                if (info.Suffix != null)
                    this.Suffix = info.Suffix.Select(r => new GenericName { Name = r }).ToList();
                else
                    this.Suffix = new List<GenericName>();
                if (info.Title != null)
                    this.Title = info.Title.Select(r => new GenericName{ Name = r}).ToList();
                else
                    this.Title = new List<GenericName>();
            }
        }

        public bool IsPopulated()
        {
            if (this.FirstName != string.Empty)
                return true;
            if (this.GeneralSuffix != string.Empty)
                return true;
            if (this.LastName != string.Empty)
                return true;
            if (this.NamePrefix != string.Empty)
                return true;
            if (this.PrecedingTitle != string.Empty)
                return true;

            if (this.GenerationIdentifier.Count > 0)
                return true;
            if (this.MiddleName.Count > 0)
                return true;
            if (this.Suffix.Count > 0)
                return true;
            if (this.Title.Count > 0)
                return true;
            if (this.NameType.HasValue)
                return true;

            return false;
        }

        public NamePerson_Type GetFATCANameType()
        {
            if (!this.IsPopulated())
                return null;

            var info = new NamePerson_Type();

            info.GeneralSuffix = this.GeneralSuffix;
            info.PrecedingTitle = this.PrecedingTitle;

            if (this.Title != null && this.Title.Count > 0) 
                info.Title = this.Title.Select(r => r.Name).ToArray();

            if (this.GenerationIdentifier != null && this.GenerationIdentifier.Count > 0)
                info.GenerationIdentifier = this.GenerationIdentifier.Select(r => r.Name).ToArray();

            if (this.Suffix != null && this.Suffix.Count > 0)
                info.Suffix = this.Suffix.Select(r => r.Name).ToArray();

            if (this.NameTypeSpecified)
                info.nameType = this.NameType.Value;
            info.nameTypeSpecified = this.NameTypeSpecified;

            if (this.FirstName.Length > 0)
                info.FirstName = new NamePerson_TypeFirstName { Value = this.FirstName };

            if (this.LastName.Length > 0)
                info.LastName = new NamePerson_TypeLastName { Value = this.LastName };

            if (this.MiddleName != null && this.MiddleName.Count > 0)
                info.MiddleName = this.MiddleName.Select(r => new NamePerson_TypeMiddleName { Value = r.Name }).ToArray();

            if (this.NamePrefix != null && this.NamePrefix.Length > 0)
                info.NamePrefix = new NamePerson_TypeNamePrefix { Value = this.NamePrefix };

            return info;
        }

        public bool Equals(PersonName other)
        {
            if (other == null) return false;
            return this.ID.Equals(other.ID);
        }
    }
    #endregion PersonName
}
#endregion Wrapper Classes
